package cn.batch.daemon.loadBatch;


import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.DocumentException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.batch.daemon.Configuration;
import cn.batch.daemon.bean.ApplicantLoanInfo;
import cn.batch.daemon.bean.ApplicantionDecisionResult;
import cn.batch.daemon.bean.ApplicantionInfo;
import cn.batch.daemon.bean.IN_PreApproval;
import cn.batch.daemon.bean.PreparApprovalxmlTool;
import cn.batch.daemon.bean.TaskMissionBean;
import cn.batch.daemon.bean.TriggerRule;
import cn.batch.daemon.blazeServer.RiskWarningServer;
import cn.batch.daemon.jdbc.MessionTaskStore;
import cn.batch.daemon.jdbc.RiskWarningStore;
import cn.batch.daemon.service.RiskWarningService;
import cn.batch.daemon.taskEnum.MissionSts;
import cn.batch.daemon.taskEnum.TaskMissionType;
import cn.batch.daemon.util.Assert;
import cn.batch.daemon.util.ConstantParameter;
import cn.batch.daemon.util.DateUtil;
import cn.batch.daemon.util.ServerFileConfig;

import com.blazesoft.server.base.NdServerException;
import com.blazesoft.server.base.NdServiceException;
import com.blazesoft.server.base.NdServiceSessionException;
import com.blazesoft.server.local.NdLocalServerException;
import com.blazesoft.util.NdWrappedException;

public class RiskWarningListener implements Runnable {
	private static final Log logger = LogFactory
			.getLog(RiskWarningListener.class);

	private boolean initialized = false;
	private String batchDate;
	private String resultFileUrl ;
	private int loadBatchSize;

	private Configuration config;
	private ApplicationContext ctx;
	private RiskWarningService riskWarningService;
	private RiskWarningServer serverService;


	/**
	 * @param config
	 */
	public RiskWarningListener(Configuration config) {
		super();
		this.config = config;
	}

	/**
	 * 初始化
	 */
	public void initialize() {
		if (ctx == null) {
			ctx = new ClassPathXmlApplicationContext(
					new String[] { "applicationContext*.xml" });
		}

		riskWarningService = (RiskWarningService) ctx
				.getBean("riskWarningService");

		resultFileUrl = config.getSetting(ConstantParameter.RISKWARN_DAY_RESULT_FILE_URL, "");
		loadBatchSize = config.getSetting(ConstantParameter.RISKWARN_LOAD_BATCH_SIZE, 10000);
		initialized = true;

	}

	/**
	 * 处理逻辑 如风险预警客户信息表有待处理数据,则 1.更新(新增)批量任务数据-表APP_BATCH_INFO
	 * 2.为避免同批次日期客户数据重新装载后跑批,先根据批次日期,删除决策信息表中数据
	 * 3.按批次日期查询待处理客户数据,循环客户数据,组装成blaze规则库所需的xml格式
	 * 4.调用blaze规则库,得到规则处理结果,解析结果至IN_PreApproval对象中,将对象放入list
	 * 5.将list中的IN_PreApproval对象存储至数据库 6.修改批量任务表(APP_BATCH_INFO)的任务状态
	 **/
	public void run() {
		initialize();
		Assert.state(initialized, "必须初始化完成才能运行");

		String remark = "";

		logger.info("开始执行风险预警批处理，批次日期为" + batchDate);
		// 待处理客户数
		int custCount = 0;
		// 触发规则客户数
		int riskWarningCount = 0;
		// 任务起始时间
		long startTime = System.currentTimeMillis();
		// 任务结束时间
		long endTime = 0;
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		TaskMissionBean tmb = new TaskMissionBean();
		try {
						
			// 1.更新(新增)批量任务数据-表APP_BATCH_INFO
			tmb = taskMissionOperate();
			
			// 2.为避免同批次日期客户数据重新装载后跑批，先根据批次日期,删除决策信息表中数据
			logger.info("开始删除数据。");
			riskWarningService.deleteAppInfoByBatchNoAndType(batchDate, TaskMissionType.FXYJ.getValue());
			
			/*ApplicantionInfo ai = new ApplicantionInfo();
			ai.setBatchNo(batchDate);
			ai.setBatchType(TaskMissionType.FXYJ.getValue());
			
			List<ApplicantionInfo> idList = riskWarningService.queryApplicantionInfo(ai);
			riskWarningService.deleteApplicantionInfo(idList);*/
			logger.info("删除数据完成。");
					
			// 3.循环客户数据，组装成blaze规则库所需的xml格式
			// 查询客户信息
			logger.info("开始查询客户数据。");
			String qDate = DateUtil.zhDateString(batchDate, "yyyyMMdd", "yyyy-MM-dd");
			custCount = RiskWarningStore.getRiskCustInfoCount(qDate);
			logger.info("获得待处理客户数 :" + custCount);
			
			serverService = (RiskWarningServer) RiskWarningServer.createServer(ServerFileConfig.SERVER_PATH);
			logger.info("规则库服务已创建成功。");
	
			int page = custCount / loadBatchSize;
			int lastPageCount = custCount % loadBatchSize;
			if (lastPageCount > 0) {
				page++;
			}
			
			List<IN_PreApproval> custList = new ArrayList<IN_PreApproval>();
			//List<IN_PreApproval> resultList = null;
			List<IN_PreApproval> resultList = new ArrayList<IN_PreApproval>();
			List<String> rule = new ArrayList<String>(10000); // 保存个贷需要的信息
			int fromRow, toRow;
			for (int i = 0; i < page; i++) {
				fromRow = i * loadBatchSize;
				toRow = (i == (page - 1)) ? fromRow + lastPageCount
						: fromRow + loadBatchSize;
				
				custList = RiskWarningStore.getRiskCustInfoPage(qDate, fromRow, toRow);
				logger.info("本次加载客户数：" + custList.size());
				logger.info("开始调用风险预警规则库。 ");
				
				resultList = triggerRule(custList);
				logger.info("触发规则客户数为： " + resultList.size());
				// 5.将list中的IN_PreApproval对象存储至数据库
				save(resultList, rule);
				logger.info("入库已完成。");
				riskWarningCount += resultList.size();
			}
			
			serverService.shutdown();
			custList.clear();
			resultList.clear();
			
			// 保存触发规则的客户的指定字段（个贷需要的字段）到文件中
			logger.info("开始写文件。");
			saveListToFile(rule);
			rule.clear();
			logger.info("写文件结束。");
			// 6.修改批量任务表(APP_BATCH_INFO)的任务状态
			remark = "本次风险预警批处理客户数为： " + custCount + ",触发规则客户数为： "
					+ riskWarningCount;
			tmb.setMissionSts(MissionSts.SUCCESS);
			tmb.setDisposeEndTm(new Timestamp(System.currentTimeMillis()));
			tmb.setRemark(remark);
			MessionTaskStore.updateImportDay(tmb);

			endTime = System.currentTimeMillis();

			long along = endTime - startTime;
			logger.info("****************************************");
			logger.info("*    本次风险预警批处理任务执行完成于： " + new java.util.Date());
			logger.info("*    本次风险预警批处理客户数为： " + custCount);
			logger.info("*    本次风险预警批触发规则客户数为： " + riskWarningCount);
			logger.info("*    本次风险预警批处理任务起始时间为： " + sdf.format(startTime));
			logger.info("*    本次风险预警批处理任务结束时间为： " + sdf.format(endTime));
			logger.info("*    共耗时 " + along + " 毫秒");
			logger.info("****************************************");

		} catch (Exception e) {
			tmb.setMissionSts(MissionSts.FAILED);
			tmb.setDisposeEndTm(new Timestamp(System.currentTimeMillis()));
			tmb.setRemark("风险预警(按天)批处理失败,请联系系统管理员解决问题");
			try {
				MessionTaskStore.updateImportDay(tmb);
			} catch (SQLException sqle) {
				logger.error("更新任务状态或标识表失败。", sqle);
			}
			
			logger.error("风险预警(按天)批处理失败。", e);
			throw new RuntimeException(e);
		}
	}

	private void save(List<IN_PreApproval> resultList, List<String> rule) throws ParseException {
		List<ApplicantionInfo> appInfoCache = new ArrayList<ApplicantionInfo>(10000);
		for(int i=0; i < resultList.size(); i++) {
			IN_PreApproval p = resultList.get(i);
			ApplicantionInfo appInfo = new ApplicantionInfo();
			appInfo.setBatchType(TaskMissionType.FXYJ.getValue());
			appInfo.setRecordDate(new Date());
			appInfo.setBatchNo(batchDate);
			appInfo.setBatchDate(DateUtil.fromString(batchDate, "yyyyMMdd"));
			appInfo.getApplicantInfos().add(p.getApplicantInfo());
			appInfo.getGuarantorInfos().add(p.getGuarantorInfo());
			ApplicantLoanInfo applicantLoanInfo = p.getApplicantLoanInfo();
			
			StringBuffer s = new StringBuffer();
			s.append(batchDate).append('|');
			s.append(applicantLoanInfo.getLoanAccount()).append('|');
			
			appInfo.getApplicantLoanInfos().add(applicantLoanInfo);
			//appInfo.getApplicantCmInfos().add(p.getApplicantCmInfo());
			appInfo.getApplicantSpouseInfos().add(p.getApplicantSpouseInfo());
			//appInfo.getApplicantPbocInfos().add(p.getApplicantPbocInfo());
			ApplicantionDecisionResult applicantionDecisionResult = p.getApplicantionDecisionResult();
			Set<TriggerRule> triggerRules = applicantionDecisionResult.getTriggerRules();
			for (TriggerRule tr : triggerRules) {
				s.append(tr.getRuleCode()).append(',');
			}
			s.deleteCharAt(s.length()-1);
			rule.add(s.toString());
			
			appInfo.getApplicantionDecisionResults().add(applicantionDecisionResult);
			appInfoCache.add(appInfo);
		}
		riskWarningService.saveAll(appInfoCache);
	}

	private List<IN_PreApproval> triggerRule(List<IN_PreApproval> custList) throws Exception {
		List<IN_PreApproval> resultList = new ArrayList<IN_PreApproval>();
		for (int i = 0; i < custList.size(); i++) {
			IN_PreApproval p = (IN_PreApproval) custList.get(i);
			// 组装XML
			String xml = makeXML(p);
			//System.out.println(">>>>>>>>" + xml);
			// 4.调用blaze规则库,得到规则处理结果,解析结果至IN_PreApproval对象中,将对象放入list
			String retVal = blazeRiskWarningServer(xml, serverService);
			//System.out.println(">>>>>>>>" + retVal);
			// 解析返回的xml文件
			IN_PreApproval in = readRiskXml(retVal,p);
			// 判断该用户有无触发blaze规则
			if (in != null) {
				resultList.add(in); // 将触发规则的客户信息及规则结果存储至数据库
			}
		}
		return resultList;
	}

	private void saveListToFile(List<String> r) throws IOException {
		
		FileOutputStream fos = null;
		OutputStreamWriter osw = null;
		BufferedWriter buffw = null;
		String pathname = "";
		try {
			pathname = resultFileUrl + batchDate + File.separator;
			File resultFile = new File(pathname + batchDate +".txt");
			File parentFile = resultFile.getParentFile();
			if (!parentFile.exists()) {
				parentFile.mkdirs();
			}
			fos = new FileOutputStream(resultFile);
			osw = new OutputStreamWriter(fos, Charset.forName("GBK"));
			buffw = new BufferedWriter(osw);
			for (String str : r) {
				buffw.write(str);
				buffw.newLine();
			}
			buffw.flush();
		} finally {
			if (buffw != null) {
				buffw.close();
			}
		}
		
		File flagFile = new File(pathname + batchDate + ".txt.OK");
		flagFile.createNewFile();
	}
	/**
	 * 调用blaze规则库,得到规则处理结果
	 * 
	 * @param xml
	 * @return
	 * @throws NdLocalServerException
	 * @throws NdServerException
	 * @throws NdServiceException
	 */
	private String blazeRiskWarningServer(String xml,
			RiskWarningServer serverService) throws NdLocalServerException,
			NdServiceSessionException, NdServiceException, NdWrappedException {
		return serverService.yjDEntry(xml);
	}

	/**
	 * 批量任务表操作
	 * 
	 * @return
	 * @throws Exception
	 */

	private TaskMissionBean taskMissionOperate() throws SQLException {
		// 更新(新增)批量任务数据-表APP_BATCH_INFO
		TaskMissionBean tmb = new TaskMissionBean();
		// 批次号
		tmb.setBatch_no(batchDate);
		// 任务类型
		tmb.setMissionTyp(TaskMissionType.FXYJ);
		// 起始时间
		tmb.setDisposeStartTm(new Timestamp(System.currentTimeMillis()));
		// 任务状态
		tmb.setMissionSts(MissionSts.DISPOSING);
		// 备注
		tmb.setRemark("");
		// 判断批次任务是否已有
		List<TaskMissionBean> list = MessionTaskStore.getBatchList(tmb);
		if (list.size() > 0) { // 如果有就更新
			MessionTaskStore.updateImportDay(tmb);
		} else {
			// 新增批次任务信息
			MessionTaskStore.insertMession(tmb);
		}
		return tmb;
	}

	/**
	 * 组装blaze规则库所需指标的xml
	 * 
	 * @param p
	 * @return
	 * @throws IOException
	 */
	private static String makeXML(IN_PreApproval p) throws IOException {
		return PreparApprovalxmlTool.writePreParApprovalXml(p);
	}

	/**
	 * 解析blaze规则处理后返回的xml结果
	 * 
	 * @param retVal
	 * @return
	 * @throws DocumentException
	 */
	private IN_PreApproval readRiskXml(String retVal,IN_PreApproval p) throws DocumentException {
		return PreparApprovalxmlTool.readRiskWarningXml(retVal,p);
	}

	public void setBatchDate(String batchDate) {
		this.batchDate = batchDate;
	}
}
